/*
* Copyright (c) 2014 Qunar.com. All Rights Reserved.
*/
package net.oschina.bilbodai.common.beanutil.property.asm.lexer.parse;

import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.CodeAppliers;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.CodeApplyResult;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.ICodeApplier;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <pre>
 *     School{
 *         Student{
 *             nameMap{
 *                 "aka":123
 *             }
 *         }
 *     }
 * </pre>
 *
 * @author qt-maodai  Date: 15-8-7 Time: 下午1:43
 * @version $Id$
 */
public class Token implements IToken {

    //student.school.nameMap.<string>id
    public Class<?> apply(MethodVisitor mv, ITokenIterator iterator, Class<?> rootClassType, TokenApplyCtx ctx) throws Exception {
        if (iterator == null) {
            throw new IllegalArgumentException("iterator 不能为 null");
        }

        Class nextBeanType = rootClassType; //School.class
        Class<?> stackType = Object.class;
        List<Class<?>> nextGenericTypes = new LinkedList<>();
        while (iterator.hasNext()) {
            String nextToken = iterator.nextToken();//student
            ICodeApplier codeApplierByToken = null;
            try {
                codeApplierByToken = CodeAppliers.getCodeApplierByToken(nextToken, nextBeanType, stackType, nextGenericTypes);
            } catch (NoSuchFieldException e) {
                if (!Map.class.isAssignableFrom(rootClassType)) { //可能是出现token包含了点号，而整个主体只是一个Map。这个时候就变成直接从Map获取值
                    throw e;
                }
                //todo 这里还需要好好考虑下，以做到 支持 类似 内层Map的key的解析
                return applyMapFallback(mv, iterator, rootClassType, ctx);
            }
            CodeApplyResult applyResult = codeApplierByToken.apply(mv, ctx);
            nextBeanType = applyResult.getNextTokenType();
            nextGenericTypes = applyResult.getNextTokenGenericTypes();
            stackType = applyResult.getStackType();
        }
        return stackType;
    }

    //当Root类型为Map时，而 key 又是以点号分割的时候，
    private Class<?> applyMapFallback(MethodVisitor mv, ITokenIterator iterator, Class<?> rootClassType, TokenApplyCtx ctx) throws Exception {
        ICodeApplier codeApplierByToken;
        mv.visitInsn(Opcodes.POP);
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        codeApplierByToken = CodeAppliers.getCodeApplierByToken(iterator.token(), rootClassType, Object.class, new LinkedList<Class<?>>());
        CodeApplyResult applyResult = codeApplierByToken.apply(mv, ctx);
        return applyResult.getStackType();
    }

}